#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "data_type.h"
#include "errno.h"
#include "alloc.h"
#include "list.h"
#include "attrlist.h"
#include "memfunc.h"
#include "basefunc.h"
#include "struct_deal.h"
#include "crypto_func.h"
#include "memdb.h"
#include "message.h"
#include "ex_module.h"
#include "channel.h"
#include "sys_func.h"

#include "tpm2_value.h"
#include "tpm2_global.h"
#include "tpm2_structure.h"
#include "tpm2_command.h"
#include "vtcm_alg.h"

#include "tpm2_utils.h"

static  BYTE Buf[DIGEST_SIZE*32];
static  BYTE Output[DIGEST_SIZE*32];

void * curr_recv_msg=NULL;

static CHANNEL * tpm2_caller;

// Ex CA Module

void * tpm2_auto_build_outputmsg(char * out_line, void * active_msg)
{ 

  RECORD(TPM2_UTILS,OUTPUT) * output_para;
  int offset=0;
  int ret;
  int i=0;
  void * send_msg;
  BYTE * out_param;	


  output_para=Talloc0(sizeof(*output_para));
  if(output_para==NULL)
    return NULL;

  do{
    out_param=&Output[DIGEST_SIZE*2*i];
    Memset(out_param,0,DIGEST_SIZE*2);
    ret=Getfiledfromstr(out_param,out_line+offset,' ',DIGEST_SIZE*2);
    if(ret>0)
    {
      i++;
      offset+=ret+1;
    }
  }while(ret>0);

  // Build output para message

  output_para->param_num=i;
  if(i>0)
  {	
    output_para->params=Talloc0(DIGEST_SIZE*2*output_para->param_num);	
    Memcpy(output_para->params,Output,DIGEST_SIZE*2*output_para->param_num); 

  }
  send_msg=message_create(TYPE_PAIR(TPM2_UTILS,OUTPUT),active_msg);
  message_add_record(send_msg,output_para);
  return send_msg;	
}


int proc_tpm2utils_input(void * sub_proc,void * recv_msg);

// proceed the tpm2 command
int proc_tpm2utils_GetRandom(void * sub_proc, void * para); 
int proc_tpm2utils_PCR_Read(void * sub_proc,void * para);
int proc_tpm2utils_PCR_Extend(void * sub_proc,void * para);
int tpm2utils_transmit(int in_len,BYTE * in, int *  out_len, BYTE * out);


int tpm2_utils_init(void * sub_proc,void * para)
{
    int ret;
    struct tpm2_utils_init_para * init_para=para;
    if(para==NULL)
	return -EINVAL;
    tpm2_caller=channel_find(init_para->channel_name);

    if(tpm2_caller==NULL)
    {
	print_cubeerr("tpm2_utils's tpm2 channel does not exist!,enter no tpm2 running state!\n");
	return 0;	
    }	
  
   return 0;
}
int TSS_gennonce(unsigned char *nonce){
  return RAND_bytes(nonce,SM3_256_DIGEST_SIZE);
}
int tpm2_utils_start(void * sub_proc,void * para)
{
  int ret;
  int retval;
  void * recv_msg;
  void * send_msg;
  void * context;
  void * sock;
  BYTE uuid[DIGEST_SIZE];
  int i;
  int type;
  int subtype;

  //	print_cubeaudit("begin proc tpm2_utils \n");

  //    ret=proc_tpm2utils_start(sub_proc,para);
  //    if(ret<0)
  //        return ret;

  while(1)
  {
    usleep(time_val.tv_usec);
    ret=ex_module_recvmsg(sub_proc,&recv_msg);
    if(ret<0)
      continue;
    if(recv_msg==NULL)
      continue;

    type=message_get_type(recv_msg);
    subtype=message_get_subtype(recv_msg);
    if((type==TYPE(TPM2_UTILS)) &&(subtype ==SUBTYPE(TPM2_UTILS,INPUT)))
    {
      ret=proc_tpm2utils_input(sub_proc,recv_msg);
    }
  }

  return 0;
};


int proc_tpm2utils_input(void * sub_proc,void * recv_msg)
{
  	int ret = 0;
  	int cmd_run=0;

  	struct tpm2_utils_input * input_para;
  	ret=message_get_record(recv_msg,&input_para,0);

  	if(ret<0)
    	return -EINVAL;

  	if(input_para->param_num<1)
  	{	
    	print_cubeerr("wrong command format! should be %s [cmd] [para] ...\n","main_proc");
    	return -EINVAL;
  	}
  	curr_recv_msg=recv_msg;
  	if(tpm2_caller!=NULL)
  	{

    	cmd_run=1;
		if(strcmp(input_para->params,"extend")==0)
  		{
 	   		ret=proc_tpm2utils_Extend(sub_proc,input_para);
  		}
		else if(strcmp(input_para->params,"pcrread")==0)
  		{
	 	   	ret=proc_tpm2utils_PCR_read(sub_proc,input_para);
  		}
  		else if(strcmp(input_para->params,"getrandom")==0)
  		{
    	    ret=proc_tpm2utils_getRandom(sub_proc,input_para);
  		}
		else
		{
			cmd_run=0;
		}
  	}

  	if(cmd_run==0)
 	// run external command
  	{

    	curr_recv_msg=NULL;
    	return ret;
	}
}

int proc_tpm2utils_getRandom(void * sub_proc, void * para){
 	int outlen;
  	int i=0;
  	int ret=0;
  	void *tpm2_template;
  	RECORD(TPM2_IN,GetRandom) *tpm2_input;
  	RECORD(TPM2_OUT,GetRandom) *tpm2_output;
  	tpm2_input = Talloc0(sizeof(*tpm2_input));
  	if(tpm2_input==NULL)
    	return -ENOMEM;
  	tpm2_output = Talloc0(sizeof(*tpm2_output));
  	if(tpm2_output==NULL)
    	return -ENOMEM;
  	tpm2_input->tag = TPM_ST_NO_SESSIONS;
  	tpm2_input->commandCode = TPM_CC_GetRandom;
  	tpm2_input->bytesRequested=0x10;
  	tpm2_template=memdb_get_template(TYPE_PAIR(TPM2_IN,GetRandom));
  	if(tpm2_template==NULL)
  		return -EINVAL;
  	tpm2_input->commandSize=sizeof(*tpm2_input);
  	ret = struct_2_blob(tpm2_input,Buf,tpm2_template);
  	if(ret<0)
    	return ret;
  	printf("Send command for getRandom:\n");
  	print_bin_data(Buf,ret,8);

  	ret = tpm2utils_transmit(tpm2_input->commandSize,Buf,&outlen,Buf);
  	if(ret<0)
    	return ret; 
  	printf("Receive  output is:\n");
  	print_bin_data(Buf,outlen,8);

  	tpm2_template=memdb_get_template(TYPE_PAIR(TPM2_OUT,GetRandom));
  	ret = blob_2_struct(Buf,tpm2_output,tpm2_template);
  	if(ret<0)
    	return ret;

  	tpm2_template=memdb_get_template(TYPE_PAIR(TPM2_OUT,GetRandom));
    struct_read_elem_text("randomBytes.buffer",tpm2_output,Output,tpm2_template);

    sprintf(Buf,"%d %d %s\n",tpm2_output->responseCode,tpm2_output->randomBytes.size,Output);
    printf("Output para: %s\n",Buf);
    void * send_msg = tpm2_auto_build_outputmsg(Buf,curr_recv_msg);
    if(send_msg == NULL)
        return -EINVAL;
    ret=ex_module_sendmsg(sub_proc,send_msg);
  	return ret;
} 

int proc_tpm2utils_Extend(void * sub_proc, void * para)
{
 	int outlen;
  	int i=0;
  	int ret=0;
	int cmdsize=0;
  	void *tpm2_template;
  	RECORD(TPM2_IN,PCR_Extend) *tpm2_input;
  	RECORD(TPM2_OUT,PCR_Extend) *tpm2_output;
  	tpm2_input = Talloc0(sizeof(*tpm2_input));
  	if(tpm2_input==NULL)
    	return -ENOMEM;
  	tpm2_output = Talloc0(sizeof(*tpm2_output));
  	if(tpm2_output==NULL)
    	return -ENOMEM;
  	tpm2_input->tag = TPM_ST_SESSIONS;
  	tpm2_input->commandCode = TPM_CC_PCR_Extend;
  	tpm2_input->pcrHandle=1;
	tpm2_input->digests.count=1;
	tpm2_input->digests.digests=Talloc0(sizeof(TPM2B_DIGEST)*tpm2_input->digests.count);
	tpm2_input->digests.digests[0].size=10;
	tpm2_input->digests.digests[0].buffer=Talloc0(tpm2_input->digests.digests[0].size);
	Memset(tpm2_input->digests.digests[0].buffer,'A',tpm2_input->digests.digests[0].size);
	
  	tpm2_template=memdb_get_template(TYPE_PAIR(TPM2_IN,PCR_Extend));
  	if(tpm2_template==NULL)
  		return -EINVAL;
  	tpm2_input->commandSize=sizeof(*tpm2_input);
  	cmdsize = struct_2_blob(tpm2_input,Buf,tpm2_template);
  	if(cmdsize<0)
    		return cmdsize;
	tpm2_input->commandSize=cmdsize;
	*(int *)(Buf+sizeof(tpm2_input->tag)) = htonl(cmdsize);
//  	cmdsize = struct_2_blob(tpm2_input,Buf,tpm2_template);
 // 	if(cmdsize<0)
  //  		return cmdsize;
  	printf("Send command for PCR_Extend:\n");
  	print_bin_data(Buf,cmdsize,8);
  	ret = tpm2utils_transmit(cmdsize,Buf,&outlen,Buf);
  	if(ret<0)
    		return ret; 
  	printf("Receive  output is:\n");
  	print_bin_data(Buf,outlen,8);
  	return ret;
} 
/*
  int i=1;
  int j=0;
  int index = -1;
  int outlen;
  int ret = 0;
  char * message = NULL;
  struct tpm2_in_extend * tpm2_input;
  struct tpm2_out_extend * tpm2_output;
  void * tpm2_template;
  unsigned char msghash[32];
  printf("Begin extend:\n");
  tpm2_input = Talloc0(sizeof(*tpm2_input));
  if(tpm2_input==NULL)
    return -ENOMEM;
  tpm2_output = Talloc0(sizeof(*tpm2_output));
  if(tpm2_output==NULL)
    return -ENOMEM;
  struct tpm2_utils_input * input_para=para;
  char * curr_para;

  while(i<input_para->param_num){
    curr_para=input_para->params+i*DIGEST_SIZE;
    if(!strcmp("-ix",curr_para)){
      i++;
      curr_para=input_para->params+i*DIGEST_SIZE;
      if(i<input_para->param_num){
        sscanf(curr_para,"%d",&tpm2_input->pcrNum);
        //	index = Atoi(input_para->argv[i]);
      }
      else{
        printf("Missing parameter for -ix.\n");
        return -1;
      }	
    }else if(!strcmp(curr_para,"-ic")){
      i++;
      curr_para=input_para->params+i*DIGEST_SIZE;
      if(i<input_para->param_num){
        message = curr_para;
      }else{
        printf("Missing parameter for -ic.\n");
      }
    }
    i++;
  }
  if(message != NULL){
    tpm2_ex_sm3(msghash, 1,message,strlen(message));
  }
  for(j=0;j<32;j++){
    tpm2_input->inDigest[j] = (BYTE)msghash[j];
  }
  tpm2_input->tag = htons(TCM_TAG_RQU_COMMAND);
  tpm2_input->ordinal=SUBTYPE_EXTEND_IN;
  tpm2_template=memdb_get_template(DTYPE_VTCM_IN,SUBTYPE_EXTEND_IN);
  if(tpm2_template==NULL)
    return -EINVAL;
  tpm2_input->paramSize=sizeof(*tpm2_input);
  ret =  struct_2_blob(tpm2_input,Buf,tpm2_template);
  if(ret<0)
    return ret;
  print_bin_data(Buf,ret,8);
  ret = tpm2utils_transmit(tpm2_input->paramSize,Buf,&outlen,Buf);
  if(ret<0){
    return ret;}
  tpm2_template=memdb_get_template(DTYPE_VTCM_OUT,SUBTYPE_EXTEND_OUT);
  if(tpm2_template==NULL)
    return -EINVAL;
  ret=blob_2_struct(Buf,tpm2_output,tpm2_template);
  if(ret<0)
    return ret;

  print_bin_data(Buf,ret,8);
  sprintf(Buf,"%d \n",tpm2_output->returnCode);
  printf("Output para: %s\n",Buf);
  void * send_msg =tpm2_auto_build_outputmsg(Buf,curr_recv_msg);
  if(send_msg==NULL)
    return -EINVAL;
  ex_module_sendmsg(sub_proc,send_msg);		

  return ret;
}*/

int proc_tpm2utils_PCR_read(void * sub_proc, void * para)
{
	return 0;
}
/*
  int i = 1;
  int ret = 0;
  int index = -1;
  int outlen;
  char * pcrfile=NULL;

  struct tpm2_in_pcrread * tpm2_input;
  struct tpm2_out_pcrread * tpm2_output;
  void * tpm2_template;

  struct tpm2_utils_input * input_para=para;
  char * index_para;
  char * value_para;

  unsigned char digest[TCM_HASH_SIZE];
  printf("begin proc pcrread \n");

  tpm2_input=Talloc0(sizeof(*tpm2_input));
  if(tpm2_input==NULL)
    return -ENOMEM;	
  tpm2_output=Talloc0(sizeof(*tpm2_output));
  if(tpm2_output==NULL)
  return -ENOMEM;	

  if((input_para->param_num>0)&&(input_para->param_num%2==1))
  {
	for(i=1;i<input_para->param_num;i+=2)
	{
       		index_para=input_para->params+i*DIGEST_SIZE;
       		value_para=index_para+DIGEST_SIZE;
		if(!Strcmp("-ix",index_para))
		{
       			sscanf(value_para,"%x",&tpm2_input->pcrIndex);
		}	
		else if(!Strcmp("-wf",index_para))
		{
  			pcrfile=value_para;
		}
		else
		{
			printf("Error cmd format! should be %s -ix pcrindex -wf pcrfile",input_para->params);
			return -EINVAL;
		}
	}
  }

  tpm2_input->tag=htons(TCM_TAG_RQU_COMMAND);
  tpm2_input->ordinal=SUBTYPE_PCRREAD_IN;		
  tpm2_template=memdb_get_template(DTYPE_VTCM_IN,SUBTYPE_PCRREAD_IN);
  if(tpm2_template==NULL){
    return -EINVAL;
  }
  tpm2_input->paramSize=sizeof(*tpm2_input);
  ret=struct_2_blob(tpm2_input,Buf,tpm2_template);
  if(ret<0)
      return ret;
  print_bin_data(Buf,ret,8);
  ret=tpm2utils_transmit(tpm2_input->paramSize,Buf,&outlen,Buf);		
  if(ret<0)
      return ret;
  tpm2_template=memdb_get_template(DTYPE_VTCM_OUT,SUBTYPE_PCRREAD_OUT);
  if(tpm2_template==NULL)
      return -EINVAL;
  ret=blob_2_struct(Buf,tpm2_output,tpm2_template);
  if(ret<0)
      return ret;
  print_bin_data(Buf,ret,8);
  printf("Pcr %d value:\n",tpm2_input->pcrIndex);
  print_bin_data(tpm2_output->outDigest,32,8);

  if(pcrfile!=NULL)
  {
	// add read pcr value to the pcrfile (TCM_PCR_COMPOSITE format)
	int fd ;
	tpm2_template=memdb_get_template(DTYPE_VTCM_PCR,SUBTYPE_TCM_PCR_COMPOSITE);
	if(tpm2_template==NULL)
		return -EINVAL;
	TCM_PCR_COMPOSITE * pcr_comp=Talloc0(sizeof(*pcr_comp));
	fd=open(pcrfile,O_RDWR);
	if(fd<0)
        {
		fd=open(pcrfile,O_RDWR|O_CREAT);
		if(fd<0)
		{
    			printf("No pik file %s!\n",pcrfile);
    			return -EINVAL;
		}
		ret=tpm2_Init_PcrComposite(pcr_comp);
		close(fd);
	}
	else
	{
		ret=read(fd,Buf,DIGEST_SIZE*32+1);
  		if(ret<0)
  		{
    			printf("can't read pcrfile data!\n");
    			return -EINVAL;
  		}
  		if(ret>DIGEST_SIZE*32)
  		{
    			printf("pcr file too long!\n");
    			return -EINVAL;
  		}
		close(fd);
		if(ret<DIGEST_SIZE)
		{
			ret=tpm2_Init_PcrComposite(pcr_comp);
		}
		else
		{
			ret=blob_2_struct(Buf,pcr_comp,tpm2_template);
			if(ret<0)
				return -EINVAL;
		}
	}

	ret=tpm2_Dup_PCRComposite(pcr_comp,tpm2_input->pcrIndex,tpm2_output->outDigest);
	if(ret<0)
		return -EINVAL;
	ret=struct_2_blob(pcr_comp,Buf,tpm2_template);
	if(ret<0)
		return ret;
  	fd=open(pcrfile,O_CREAT|O_TRUNC|O_WRONLY,0666);
  	if(fd<0)
    		return -EIO;
  	write(fd,Buf,ret);
  	close(fd);
	
  }


  sprintf(Buf,"%d \n",tpm2_output->returnCode);

  printf("Output para: %s\n",Buf);
  void * send_msg =tpm2_auto_build_outputmsg(Buf,curr_recv_msg);
  if(send_msg==NULL)
      return -EINVAL;
  ex_module_sendmsg(sub_proc,send_msg);		

  return ret;
} */

int tpm2utils_transmit(int in_len,BYTE * in, int * out_len, BYTE * out)
{
  	int ret;
        ret=channel_write(tpm2_caller,in,in_len);
	if(ret!=in_len)
		return -EINVAL;
	for(;;)
	{
        	usleep(time_val.tv_usec);
		ret=channel_read(tpm2_caller,out,DIGEST_SIZE*32);
		if(ret<0)
			return ret;
		if(ret>0)
		{
			*out_len=ret;
			break;
		}	
	}	
	
  	return ret;
}
